package coco.wx.com.aspect;

import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;

/**
 * 日志打印
 */
@Aspect
@Component
public class ControllerLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);

    // controller层的统计日志/耗时(方法所在的包)
    private static final String POINT = "execution(public * coco.wx.com.module.*.controller.*.*(..)))";


    @AfterThrowing(pointcut = POINT, throwing = "e")
    public void handleThrowing(JoinPoint joinPoint, Exception e) {

        //通过uuid关联请求参数和返回参数
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        // 打印请求内容
        try {
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert attributes != null;
            HttpServletRequest request = attributes.getRequest();
            StringBuffer requestURL = request.getRequestURL();
            String remoteAddr = request.getRemoteAddr();
            String method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
            LOGGER.error("\n------------------------------------------------\n");
            LOGGER.error("[{}]请求URL : {}", uuid, requestURL);
            LOGGER.error("[{}]请求IP  : {}", uuid, remoteAddr);
            LOGGER.error("[{}]请求方法 : {}", uuid, method);
            // 只记录post方法
            if ("POST".equals(request.getMethod()) && ((request.getHeader("Content-Type").toLowerCase()).contains("application/json") || (request.getHeader("Content-Type").toLowerCase()).contains("form"))) {
                String parms;
                // 获取参数, 只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管
                if (joinPoint.getArgs().length > 0) {
                    for (Object o : joinPoint.getArgs()) {
                        if (o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof ModelMap) {
                            continue;
                        }
                        parms = JSONObject.toJSONString(o);
                        LOGGER.error("[{}]请求参数 : {} ", uuid, parms);
                    }
                }

            } else {
                LOGGER.error("[{}]请求参数 : {}", uuid, getParamString(request.getParameterMap()));
                LOGGER.error("\n");
            }
            LOGGER.error("[{}]异常信息 : {}", uuid, e.getMessage(), e);
        } catch (Exception ee) {
            LOGGER.error("[{}]AOP methodBefore异常:{}", uuid, ee);
        }

    }

    /**
     * 用于日志拦截
     */
    @Around(POINT)
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //通过uuid关联请求参数和返回参数
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        //开始时间一定要写在proceed之前
        long startTime = System.currentTimeMillis();
        Object proceed = pjp.proceed();
        methodBefore(pjp, uuid);
        try {

            methodAfterReturing(proceed, uuid, startTime);
        } catch (Exception e) {
            LOGGER.error("[{}]Response异常内容:{}", uuid, e);
            throw e;
        }
        return proceed;
    }

    private void methodBefore(JoinPoint joinPoint, String uuid) {
        // 打印请求内容
        try {
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert attributes != null;
            HttpServletRequest request = attributes.getRequest();
            StringBuffer requestURL = request.getRequestURL();
            String remoteAddr = request.getRemoteAddr();
            String method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
            LOGGER.info("\n------------------------------------------------\n");
            LOGGER.info("[{}]请求URL : {}", uuid, requestURL);
            LOGGER.info("[{}]请求IP  : {}", uuid, remoteAddr);
            LOGGER.info("[{}]请求方法 : {}", uuid, method);
            // 只记录post方法
            if ("POST".equals(request.getMethod()) && ((request.getHeader("Content-Type").toLowerCase()).contains("application/json") || (request.getHeader("Content-Type").toLowerCase()).contains("form"))) {
                String parms;
                // 获取参数, 只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管
                if (joinPoint.getArgs().length > 0) {
                    for (Object o : joinPoint.getArgs()) {
                        if (o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof ModelMap) {
                            continue;
                        }
                        parms = JSONObject.toJSONString(o);
                        LOGGER.info("[{}]请求参数 : {} ", uuid, parms);
                    }
                }
            } else {
                LOGGER.info("[{}]请求参数 : {}", uuid, getParamString(request.getParameterMap()));
                LOGGER.info("\n");
            }
        } catch (Exception e) {
            LOGGER.error("[{}]AOP methodBefore异常:{}", uuid, e);
        }
    }

    private void methodAfterReturing(Object o, String uuid, Long startTime) {
        try {
            if (o != null) {
                long endTime = System.currentTimeMillis();
                LOGGER.info("[{}]接口耗时 :[{}]毫秒", uuid, (endTime - startTime));
                LOGGER.info("[{}]返回参数 :\n{}", uuid, JSONObject.toJSON(o));
            }
        } catch (Exception e) {
            LOGGER.error("[{}]AOP methodAfterReturing:", uuid, e);
        }
    }


    private String getParamString(Map<String, String[]> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String[]> e : map.entrySet()) {
            sb.append(e.getKey()).append("=");
            String[] value = e.getValue();
            if (value != null && value.length == 1) {
                sb.append(value[0]).append("\t");
            } else {
                sb.append(Arrays.toString(value)).append("\t");
            }
        }
        return sb.toString();
    }
}
